/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#pragma once
#include <baja/stream/storage_file_interface.h>
#include <baja/model/generic_model.h>
#include <baja/transition_tracking/tracked_object.h>
#include <journal/app/constants.h>
#include <journal/models/interfaces.h>
#include <journal/models/internal_interfaces.h>

namespace journal { namespace model {

namespace b = baja;
namespace bm = baja::model;
namespace s = baja::storage;
namespace bt = baja::transition_tracking;

class photo_model :
    public bt::tracked_object,
    public iphoto_model,
    public iinternal_photo_model,
    public std::enable_shared_from_this<photo_model>
{
public:
    // constructor/destructor
    photo_model(
        const b::uint32& typeToken,
        const std::shared_ptr<ipage_model>& parent, 
        std::shared_ptr<journal::model::db::iphoto_table> photoTable,
        std::shared_ptr<journal::model::db::imedia_file_table> mediaFileTable,
        b::uint32 photoId);

    static std::shared_ptr<photo_model> create(
        const b::uint32& typeToken,
        const std::shared_ptr<ipage_model>& parent, 
        const std::shared_ptr<journal::model::db::iphoto_table>& photoTable,
        const std::shared_ptr<journal::model::db::imedia_file_table>& mediaFileTable,
        const b::uint32& photoId);

    static void removeFromDB(
        const std::shared_ptr<journal::model::db::iphoto_table>& photoTable,
        const b::uint32 pageId);

    // iphoto_model

    b::int32 addObserver(const std::shared_ptr<iphoto_model_observer>& observer);
    void removeObserver(const std::shared_ptr<iphoto_model_observer>& observer);
    void removeObserver(const b::int32 cookie);

    b::uint32 storageId();
    std::shared_ptr<ipage_model> parent();

    std::wstring textureFile();
    std::shared_ptr<b::imemory_stream> textureFileStream();
    const b::float32 scale();
    const m::point2d& position();
    const b::float32 angle();

    void setSpatialAttributes(
        const b::float32& scale,
        const m::point2d& position,
        const b::float32& angle
        );

    DEBUG_MODEL_DECLARATION;

    // iinternal_photo_model
    void initializeAsync(
        const std::shared_ptr<baja::storage::istorage_file>& originalStorageFile,
        const b::float32& scale,
        const m::point2d& position,
        const b::float32& angle,
        const std::function<void()>& initializeCompleted);

    void loadAsync(std::function<void()> loadCompleted);
    void unload();
    void createInDB();
    void activate(
        const b::Boolean& active);
    void dispose();

    // destructor
    ~photo_model();

private:

    // state
    b::Boolean _inDB;
    b::Boolean _initialized;
    b::Boolean _disposed;
    std::recursive_mutex _mutex;

    // persistence support
    b::uint32 _photoId;
    std::shared_ptr<journal::model::db::iphoto_table> _photoTable;

    static b::uint32 _maxMediaFileId;
    b::uint32 _mediaFileId;
    std::shared_ptr<journal::model::db::imedia_file_table> _mediaFileTable;

    std::shared_ptr<s::iengine> _dbEngine;

    static const b::uint32 photo_model::_photosReadyPass;
    HANDLE _readyEvent;

    // tree data structure support
    std::weak_ptr<ipage_model> _parent;

    // file copying support...
    std::wstring _localFileName;
    std::shared_ptr<s::istorage_file> _localStorageFile;
    std::shared_ptr<b::imemory_stream> _localStorageFileStream;

    // attributes
    b::Boolean _active;
    b::float32 _scale;
    b::float32 _angle;
    m::point2d _position;

    // observers
    b::observable_source<iphoto_model_observer> _observerSource;

    // private methods
    b::Boolean isAccessible();

    void initializeLocalFileAsync(
        std::wstring fileName,
        const std::function<void()>& initializeCompleted);

    void initializeMaxMediaFileId();
    void acquireNewMediaFileId();

    void markReady();

    void setAllAttributes(
        const b::uint32& mediaFileId,
        const std::shared_ptr<baja::storage::istorage_file>& localStorageFile,
        const std::shared_ptr<b::imemory_stream>& memoryStream,
        const b::float32& scale,
        const m::point2d& position,
        const b::float32& angle);

    void saveUpdatedSelf();
    void loadSelf();

    void ensureLocalFileAsync(
        const std::shared_ptr<b::storage::istorage_file>& originalStorageFile,
        const std::function<void(std::shared_ptr<b::storage::istorage_file> localStorageFile,
                                 std::shared_ptr<b::imemory_stream> localStorageFileStream)>& ensureCompleted);

    void insertMediaFileRecord(
        std::wstring originalFullFileName,
        std::wstring localFullFileName);
    
    std::wstring generateLocalFileName(std::wstring originalFileName);
    std::wstring generateLocalFullFileName(std::wstring fileName);

    void setLocalFile(
        const std::shared_ptr<b::storage::istorage_file>& localStorageFile,
        const std::shared_ptr<b::imemory_stream>& memoryStream);

    void firePhotoMovedEvent();

    bool isFileLocal(std::wstring fullFileName);

    std::wstring stampFileName(
        const std::wstring& fileName,
        const long long& value);

    // undo redo support
    void registerUpdate(
        const b::Boolean& newActive,
        const std::shared_ptr<baja::storage::istorage_file>& newLocalStorageFile,
        const std::shared_ptr<b::imemory_stream>& newLocalStorageFileStream,
        const b::float32& newScale,
        const m::point2d& newPosition,
        const b::float32& newAngle);

    void registerReady();

    void undoSelfAction(
        const std::shared_ptr<bt::itransition_parameters>& transitionParameters);

    void redoSelfAction(
        const std::shared_ptr<bt::itransition_parameters>& transitionParameters);
};

std::shared_ptr<iphoto_model> createPhoto(
    const b::uint32& typeToken,
    const std::shared_ptr<ipage_model>& parent, 
    const std::shared_ptr<journal::model::db::iphoto_table>& photoTable,
    const std::shared_ptr<journal::model::db::imedia_file_table>& mediaFileTable,
    const b::uint32& photoId);

void removePhotos(
    const std::shared_ptr<journal::model::db::iphoto_table>& photoTable,
    const b::uint32& pageId);

}} // namespace journal::model